using System;
using System.Collections.Generic;
using System.Text;

namespace MosziNET.Persistence
{

    #region / UniqueIDGenerator /

    /// <summary>
    /// A NON thread safe approach to generate unique IDs
    /// </summary>
    internal static class UniqueIDGenerator
    {
        static int id = 0;

        /// <summary>
        /// Returns a non thread safe unique ID
        /// </summary>
        public static int UniqueID
        {
            get
            {
                return id++;
            }
        }
    }

    #endregion / UniqueIDGenerator /

    #region / FindCondition abstract class /

    /// <summary>
    /// Defines a base class for all the conditions that can be used
    /// with the find method
    /// </summary>
    public abstract class FindCondition
    {
        /// <summary>
        /// Returns all the parameters used in this condition
        /// </summary>
        /// <returns></returns>
        public abstract string[] Parameters { get; }

        /// <summary>
        /// Returns the parameter value for the specified parameter
        /// </summary>
        /// <param name="parameterName"></param>
        /// <returns></returns>
        public abstract object GetParameterValue(string parameterName);

        /// <summary>
        /// Returns the condition's text
        /// </summary>
        /// <returns></returns>
        public abstract string ConditionText { get; }

        /// <summary>
        /// Returns a unique cache key for the condition
        /// </summary>
        /// <returns></returns>
        public abstract string CacheKey { get; }

        /// <summary>
        /// Returns an empty condition
        /// </summary>
        public static FindCondition Empty
        {
            get
            {
                return new EmptyCondition();
            }
        }
    }

    public class EmptyCondition : FindCondition
    {
        public override string[] Parameters
        {
            get { return new string[0]; }
        }

        public override object GetParameterValue(string parameterName)
        {
            throw new ArgumentException();
        }

        public override string ConditionText
        {
            get { return "1 = 1"; }
        }

        public override string CacheKey
        {
            get { return String.Empty; }
        }
    }

    #endregion / FindCondition abstract class /


    #region / IsLessCondition /

    /// <summary>
    /// The column is less then the specified value (or equal)
    /// </summary>
    public class IsLessCondition<T> : FindCondition
    {
        T value;
        string columnName;
        string parameterName;
        bool canBeEqual;

        private const string condition = "{0} {1} {2}";

        /// <summary>
        /// Builds a new is less condition
        /// </summary>
        /// <param name="columnName"></param>
        /// <param name="value"></param>
        /// <param name="canBeEqual"></param>
        public IsLessCondition(string columnName, T value, bool canBeEqual)
        {
            this.value = value;
            this.columnName = columnName;
            this.parameterName = "@" + columnName + UniqueIDGenerator.UniqueID;
            this.canBeEqual = canBeEqual;
        }

        /// <summary>
        /// Returns the parameter list
        /// </summary>
        /// <returns></returns>
        public override string[] Parameters
        {
            get
            {
                return new string[] { this.parameterName };
            }
        }

        /// <summary>
        /// Returns the parameter value
        /// </summary>
        /// <param name="parameterName"></param>
        /// <returns></returns>
        public override object GetParameterValue(string parameterName)
        {
            if (parameterName == this.parameterName)
                return this.value;

            throw new ArgumentException();
        }

        /// <summary>
        /// Returns the condition text
        /// </summary>
        /// <returns></returns>
        public override string ConditionText
        {
            get
            {
                return String.Format(condition,
                    this.columnName,
                    canBeEqual ? "<=" : "<",
                    this.parameterName);
            }
        }

        /// <summary>
        /// Returns a unique cache key for this condition
        /// </summary>
        /// <returns></returns>
        public override string CacheKey
        {
            get
            {
                return ConditionText.Replace(this.parameterName, String.Empty);
            }
        }
    }

    #endregion / IsLessCondition /

    #region / IsMoreCondition /

    /// <summary>
    /// The column is more then the specified value (or equal)
    /// </summary>
    public class IsMoreCondition<T> : FindCondition
    {
        T value;
        string columnName;
        string parameterName;
        bool canBeEqual;

        private const string condition = "{0} {1} {2}";

        /// <summary>
        /// Builds a new is more condition
        /// </summary>
        /// <param name="columnName"></param>
        /// <param name="value"></param>
        /// <param name="canBeEqual"></param>
        public IsMoreCondition(string columnName, T value, bool canBeEqual)
        {
            this.value = value;
            this.columnName = columnName;
            this.parameterName = "@" + columnName + UniqueIDGenerator.UniqueID;
            this.canBeEqual = canBeEqual;
        }

        /// <summary>
        /// Returns the parameter list
        /// </summary>
        /// <returns></returns>
        public override string[] Parameters
        {
            get
            {
                return new string[] { this.parameterName };
            }
        }

        /// <summary>
        /// Returns the parameter value
        /// </summary>
        /// <param name="parameterName"></param>
        /// <returns></returns>
        public override object GetParameterValue(string parameterName)
        {
            if (parameterName == this.parameterName)
                return this.value;

            throw new ArgumentException();
        }

        /// <summary>
        /// Returns the condition text
        /// </summary>
        /// <returns></returns>
        public override string ConditionText
        {
            get
            {
                return String.Format(condition,
                    this.columnName,
                    canBeEqual ? ">=" : ">",
                    this.parameterName);
            }
        }

        /// <summary>
        /// Returns a unique cache key for this condition
        /// </summary>
        /// <returns></returns>
        public override string CacheKey
        {
            get
            {
                return ConditionText.Replace(this.parameterName, String.Empty);
            }
        }

    }

    #endregion / IsMoreCondition /

    #region / EqualsCondition /

    /// <summary>
    /// The column equals the specified value
    /// </summary>
    public class EqualsCondition<T> : FindCondition
    {
        T value;
        string columnName;
        string parameterName;

        private const string condition = "{0} = {1}";

        /// <summary>
        /// Builds a new equals condition
        /// </summary>
        /// <param name="columnName"></param>
        /// <param name="value"></param>
        public EqualsCondition(string columnName, T value)
        {
            this.value = value;
            this.columnName = columnName;
            this.parameterName = "@" + columnName + UniqueIDGenerator.UniqueID;
        }

        /// <summary>
        /// Returns the parameter list
        /// </summary>
        /// <returns></returns>
        public override string[] Parameters
        {
            get
            {
                return new string[] { this.parameterName };
            }
        }

        /// <summary>
        /// Returns the parameter value
        /// </summary>
        /// <param name="parameterName"></param>
        /// <returns></returns>
        public override object GetParameterValue(string parameterName)
        {
            if (parameterName == this.parameterName)
                return this.value;

            throw new ArgumentException();
        }

        /// <summary>
        /// Returns the condition text
        /// </summary>
        /// <returns></returns>
        public override string ConditionText
        {
            get
            {
                return String.Format(condition,
                    this.columnName, this.parameterName);
            }
        }

        /// <summary>
        /// Returns a unique cache key for this condition
        /// </summary>
        /// <returns></returns>
        public override string CacheKey
        {
            get
            {
                return ConditionText.Replace(this.parameterName, String.Empty);
            }
        }

    }

    #endregion / IsMoreCondition /

    #region / UnionCondition /

    /// <summary>
    /// The union type of a union condition
    /// </summary>
    public enum UnionType
    {
        And,
        Or
    }

    /// <summary>
    /// Implements a union condition
    /// </summary>
    public class UnionCondition : FindCondition
    {
        FindCondition[] conditions;
        UnionType unionType;
        Dictionary<string, FindCondition> parameterCondition;

        /// <summary>
        /// Builds a new union condition
        /// </summary>
        /// <param name="unionType"></param>
        /// <param name="conditions"></param>
        public UnionCondition(UnionType unionType, params FindCondition[] conditions)
        {
            this.conditions = conditions;
            this.unionType = unionType;

            SetupConditions();
        }

        /// <summary>
        /// Returns all the parameters in the union condition
        /// </summary>
        /// <returns></returns>
        public override string[] Parameters
        {
            get
            {
                string[] parameters = new string[parameterCondition.Keys.Count];
                parameterCondition.Keys.CopyTo(parameters, 0);

                return parameters;
            }
        }

        /// <summary>
        /// Returns the parameter value of a parameter
        /// </summary>
        /// <param name="parameterName"></param>
        /// <returns></returns>
        public override object GetParameterValue(string parameterName)
        {
            FindCondition condition = parameterCondition[parameterName];

            return condition.GetParameterValue(parameterName);
        }

        /// <summary>
        /// Returns the condition text
        /// </summary>
        /// <returns></returns>
        public override string ConditionText
        {
            get
            {
                string unionText = " and ";
                if (this.unionType == UnionType.Or)
                    unionText = " or ";

                string[] conditionTexts = new string[conditions.Length];
                for (int i = 0; i < conditions.Length; i++)
                {
                    conditionTexts[i] = "(" + conditions[i].ConditionText + ")";
                }

                return String.Join(unionText, conditionTexts);
            }
        }

        /// <summary>
        /// Returns a unique cache key for this condition
        /// </summary>
        /// <returns></returns>
        public override string CacheKey
        {
            get
            {
                string cacheKey = String.Empty;
                for (int i = 0; i < conditions.Length; i++)
                    cacheKey += conditions[i].CacheKey;

                return cacheKey;
            }
        }

        /// <summary>
        /// Sets up the conditions
        /// </summary>
        private void SetupConditions()
        {
            parameterCondition = new Dictionary<string, FindCondition>();
            for (int i = 0; i < conditions.Length; i++)
            {
                string[] parameters = conditions[i].Parameters;
                for (int j = 0; j < parameters.Length; j++)
                {
                    parameterCondition.Add(parameters[j], conditions[i]);
                }
            }
        }
    }

    #endregion / UnionCondition /


    #region / IsNullCondition /

    /// <summary>
    /// Specifies a condition if the column is null or not null
    /// </summary>
    public class IsNullCondition : FindCondition
    {
        bool isNull;
        string columnName;
        string parameterName;

        private const string condition = "{0} IS {1} NULL";

        /// <summary>
        /// Builds a new equals condition
        /// </summary>
        /// <param name="columnName"></param>
        /// <param name="isNull"></param>
        public IsNullCondition(string columnName, bool isNull)
        {
            this.isNull = isNull;
            this.columnName = columnName;
            this.parameterName = "@" + columnName + UniqueIDGenerator.UniqueID;
        }

        /// <summary>
        /// Returns the parameter list
        /// </summary>
        /// <returns></returns>
        public override string[] Parameters
        {
            get
            {
                return new string[] { this.parameterName };
            }
        }

        /// <summary>
        /// Returns the parameter value
        /// </summary>
        /// <param name="parameterName"></param>
        /// <returns></returns>
        public override object GetParameterValue(string parameterName)
        {
            if (parameterName == this.parameterName)
                return this.isNull;

            throw new ArgumentException();
        }

        /// <summary>
        /// Returns the condition text
        /// </summary>
        /// <returns></returns>
        public override string ConditionText
        {
            get
            {
                return String.Format(condition,
                    this.columnName, this.isNull ? String.Empty : " NOT ");
            }
        }

        /// <summary>
        /// Returns a unique cache key for this condition
        /// </summary>
        /// <returns></returns>
        public override string CacheKey
        {
            get
            {
                return ConditionText.Replace(this.parameterName, String.Empty);
            }
        }

    }

    #endregion / IsNullCondition /



    #region / OrderStatement /

    /// <summary>
    /// The type of the order
    /// </summary>
    public enum OrderType
    {
        Ascending,
        Descending
    }

    /// <summary>
    /// Defines the order statement
    /// </summary>
    public class OrderStatement
    {
        string columnName;
        OrderType orderType;

        /// <summary>
        /// Builds a new instance of the order statement
        /// </summary>
        /// <param name="columnName"></param>
        /// <param name="orderType"></param>
        public OrderStatement(string columnName, OrderType orderType)
        {
            this.columnName = columnName;
            this.orderType = orderType;
        }

        /// <summary>
        /// Returns the order statements text
        /// </summary>
        public string OrderStatementText
        {
            get
            {
                return String.Format("{0} {1}",
                    this.columnName, OrderType.Ascending == this.orderType ? "" : "DESC");
            }
        }

        /// <summary>
        /// Returns an emtpy order statement
        /// </summary>
        public static OrderStatement Empty
        {
            get
            {
                return new OrderStatement(String.Empty, OrderType.Ascending);
            }
        }
    }

    #endregion / OrderStatement /
}
